/*
 * Test program to try unicode file names.
 *
 * Copyright (C) 1998-2019 Toni Ronkko
 * This file is part of dirent.  Dirent may be freely distributed
 * under the MIT license.  For all details and documentation, see
 * https://github.com/tronkko/dirent
 */

/* Silence warning about fopen being insecure */
#define _CRT_SECURE_NO_WARNINGS

#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <time.h>
#include <locale.h>

#undef NDEBUG
#include <assert.h>

int main(int argc, char *argv[])
{
#ifdef WIN32
	wchar_t wpath[MAX_PATH+1];
	char path[MAX_PATH+1];
	DWORD i, j, k, x;
	BOOL ok;
	HANDLE fh;
	_WDIR *wdir;
	struct _wdirent *wentry;
	DIR *dir;
	struct dirent *entry;
	char buffer[100];
	FILE *fp;
	int counter = 0;

	(void) argc;
	(void) argv;


	/* Initialize random number generator */
	srand(((int) time(NULL)) * 257 + ((int) GetCurrentProcessId()));

	/* Set current locale */
	if (argc > 1) {
		printf("Locale %s\n", argv[1]); 
		setlocale(LC_ALL, argv[1]);
	} else {
		setlocale(LC_ALL, "");
	}


	/****** CREATE FILE WITH UNICODE FILE NAME ******/

	/* Get path to temporary directory (wide-character and ascii) */
	i = GetTempPathW(MAX_PATH, wpath);
	assert(i > 0);
	j = GetTempPathA(MAX_PATH, path);
	assert(j > 0);

	/* Append random directory name */
	for (k = 0; k < 10; k++) {
		char c;

		/* Generate random character */
		c = "abcdefghijklmnopqrstuvwxyz"[rand() % 26];

		/* Append character to paths */
		assert(i < MAX_PATH  &&  j < MAX_PATH);
		wpath[i++] = c;
		path[j++] = c;
	}

	/* Terminate paths */
	assert(i < MAX_PATH  &&  j < MAX_PATH);
	wpath[i] = '\0';
	path[j] = '\0';

	/* Remember the end of directory name */
	k = i;

	/* Create directory using unicode */
	ok = CreateDirectoryW(wpath, NULL);
	if (!ok) {
		DWORD e = GetLastError();
		wprintf(L"Cannot create directory %ls (code %u)\n", wpath, e);
		abort();
	}

	/* Overwrite zero terminator with path separator */
	assert(i < MAX_PATH  &&  j < MAX_PATH);
	wpath[i++] = '\\';

	/* Append a few unicode characters */
	assert(i < MAX_PATH);
	wpath[i++] = 0x6d4b;
	assert(i < MAX_PATH);
	wpath[i++] = 0x8bd5;

	/* Terminate string */
	assert(i < MAX_PATH);
	wpath[i] = '\0';

	/* Create file with unicode */
	fh = CreateFileW(
		wpath,
		/* Access */ GENERIC_READ | GENERIC_WRITE,
		/* Share mode */ 0,
		/* Security attributes */ NULL,
		/* Creation disposition */ CREATE_NEW,
		/* Attributes */ FILE_ATTRIBUTE_NORMAL,
		/* Template files */ NULL
		);
	assert(fh != INVALID_HANDLE_VALUE);

	/* Write some data to file */
	ok = WriteFile(
		/* File handle */ fh,
		/* Pointer to data */ "hep\n",
		/* Number of bytes to write */ 4,
		/* Number of bytes written */ NULL,
		/* Overlapped */ NULL
		);
	assert(ok);

	/* Close file */
	ok = CloseHandle(fh);
	assert(ok);


	/****** MAKE SURE THAT UNICODE FILE CAN BE READ BY _WREADDIR ******/

	/* Zero terminate wide-character path and open directory stream */
	wpath[k] = '\0';
	wdir = _wopendir(wpath);
	if (wdir == NULL) {
		wprintf(L"Cannot open directory %ls\n", wpath);
		abort();
	}

	/* Read through entries */
	counter = 0;
	while ((wentry = _wreaddir(wdir)) != NULL) {

		/* Skip pseudo directories */
		if (wcscmp(wentry->d_name, L".") == 0) {
			continue;
		}
		if (wcscmp(wentry->d_name, L"..") == 0) {
			continue;
		}

		/* Found a file */
		counter++;
		assert(wentry->d_type == DT_REG);

		/* Append file name to path */
		i = k;
		assert(i < MAX_PATH);
		wpath[i++] = '\\';
		x = 0;
		while (wentry->d_name[x] != '\0') {
			assert(i < MAX_PATH);
			wpath[i++] = wentry->d_name[x++];
		}
		assert(i < MAX_PATH);
		wpath[i] = '\0';

		/* Open file for read */
		fh = CreateFileW(
			wpath,
			/* Access */ GENERIC_READ,
			/* Share mode */ 0,
			/* Security attributes */ NULL,
			/* Creation disposition */ OPEN_EXISTING,
			/* Attributes */ FILE_ATTRIBUTE_NORMAL,
			/* Template files */ NULL
			);
		assert(fh != INVALID_HANDLE_VALUE);

		/* Read data from file */
		ok = ReadFile(
			/* File handle */ fh,
			/* Output buffer */ buffer,
			/* Max number of bytes to read */ sizeof(buffer) - 1,
			/* Number of bytes actually read */ &x,
			/* Overlapped */ NULL
		);
		assert(ok);

		/* Make sure that we got the file contents right */
		assert(x == 4);
		assert(buffer[0] == 'h');
		assert(buffer[1] == 'e');
		assert(buffer[2] == 'p');
		assert(buffer[3] == '\n');

		/* Close file */
		ok = CloseHandle(fh);
		assert(ok);

	}
	assert(counter == 1);

	/* Close directory */
	_wclosedir(wdir);


	/****** MAKE SURE THAT UNICODE FILE NAME CAN BE READ BY READDIR *****/

	/* Zero terminate ascii path and open directory stream */
	k = j;
	path[k] = '\0';
	dir = opendir(path);
	if (dir == NULL) {
		fprintf(stderr, "Cannot open directory %s\n", path);
		abort();
	}

	/* Read through entries */
	counter = 0;
	while ((entry = readdir(dir)) != NULL) {

		/* Skip pseudo directories */
		if (strcmp(entry->d_name, ".") == 0) {
			continue;
		}
		if (strcmp(entry->d_name, "..") == 0) {
			continue;
		}

		/* Found a file */
		counter++;
		assert(entry->d_type == DT_REG);

		/* Append file name to path */
		j = k;
		assert(j < MAX_PATH);
		path[j++] = '\\';
		x = 0;
		while (entry->d_name[x] != '\0') {
			assert(j < MAX_PATH);
			path[j++] = entry->d_name[x++];
		}
		assert(j < MAX_PATH);
		path[j] = '\0';

		/* Open file for read */
		fp = fopen(path, "r");
		if (!fp) {
			fprintf(stderr, "Cannot open file %s\n", path);
			abort();
		}

		/* Read data from file */
		if (fgets(buffer, sizeof(buffer), fp) == NULL) {
			fprintf(stderr, "Cannot read file %s\n", path);
			abort();
		}

		/* Make sure that we got the file contents right */
		assert(buffer[0] == 'h');
		assert(buffer[1] == 'e');
		assert(buffer[2] == 'p');
		assert(buffer[3] == '\n');
		assert(buffer[4] == '\0');

		/* Close file */
		fclose(fp);

	}
	assert(counter == 1);

	/* Close directory */
	closedir(dir);


	/****** CREATE FILE WITH UTF-8 ******/

	/* Append UTF-8 file name (åäö.txt) to path */
	j = k;
	path[j++] = '\\';
	path[j++] = 0xc3;
	path[j++] = 0xa5;
	path[j++] = 0xc3;
	path[j++] = 0xa4;
	path[j++] = 0xc3;
	path[j++] = 0xb6;
	path[j++] = 0x2e;
	path[j++] = 0x74;
	path[j++] = 0x78;
	path[j++] = 0x74;
	assert(j < MAX_PATH);
	path[j] = '\0';

	/*
	 * Create file.
	 *
	 * Be ware that the code below creates a different file depending on
	 * the current locale!  For example, if the current locale is
	 * english_us.65001, then the file name will be "åäö.txt" (7
	 * characters).  However, if the current locale is english_us.1252,
	 * then the file name will be "ÃċÃĊÃ¶.txt" (10 characters).
	 */
	printf("Creating %s\n", path);
	fp = fopen(path, "w");
	if (!fp) {
		fprintf(stderr, "Cannot open file %s\n", path);
		abort();
	}
	fputs("hep\n", fp);
	fclose(fp);

	/* Open directory again */
	path[k] = '\0';
	dir = opendir(path);
	if (dir == NULL) {
		fprintf(stderr, "Cannot open directory %s\n", path);
		abort();
	}

	/* Read through entries */
	counter = 0;
	while ((entry = readdir(dir)) != NULL) {

		/* Skip pseudo directories */
		if (strcmp(entry->d_name, ".") == 0) {
			continue;
		}
		if (strcmp(entry->d_name, "..") == 0) {
			continue;
		}

		/* Found a file */
		counter++;
		assert(entry->d_type == DT_REG);

		/* Append file name to path */
		j = k;
		assert(j < MAX_PATH);
		path[j++] = '\\';
		x = 0;
		while (entry->d_name[x] != '\0') {
			assert(j < MAX_PATH);
			path[j++] = entry->d_name[x++];
		}
		assert(j < MAX_PATH);
		path[j] = '\0';

		/* Print file name for debugging */
		printf("Opening \"%s\" hex ", path + k + 1);
		x = 0;
		while (entry->d_name[x] != '\0') {
			printf("0x%02x ",
				(unsigned) (entry->d_name[x++] & 0xff));
		}
		printf("\n");

		/* Open file for read */
		fp = fopen(path, "r");
		if (!fp) {
			fprintf(stderr, "Cannot open file %s\n", path);
			abort();
		}

		/* Read data from file */
		if (fgets(buffer, sizeof(buffer), fp) == NULL) {
			fprintf(stderr, "Cannot read file %s\n", path);
			abort();
		}

		/* Make sure that we got the file contents right */
		assert(buffer[0] == 'h');
		assert(buffer[1] == 'e');
		assert(buffer[2] == 'p');
		assert(buffer[3] == '\n');
		assert(buffer[4] == '\0');

		/* Close file */
		fclose(fp);

	}
	assert(counter == 2);

	/* Close directory */
	closedir(dir);

#else

	/* Linux */
	(void) argc;
	(void) argv;

#endif
	return EXIT_SUCCESS;
}
